﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Abp.Domain.Services;
using Abp.UI;
using AVISOutBound.SSOServer.Payment.Wechat;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.TenPayLibV3;

namespace AVISOutBound.SSOServer.Payment
{
    public class WechatManager: DomainService,IWechatManager
    {
        /// <summary>
        /// 微信支付设置
        /// </summary>
        public readonly WechatPayOptions _options;

        public WechatManager(IOptions<WechatPayOptions> options)
        {
            _options = options.Value;
        }

        #region GetPaySign(获取预支付交易ID的支付签名)

        /// <summary>
        /// 获取预支付交易ID的支付签名
        /// </summary>
        /// <param name="appid">应用ID</param>
        /// <param name="key">微信支付——商家支付密钥</param>
        /// <param name="prepayId">预支付ID。通过调用Pay()获取</param>
        /// <returns></returns>
        public async Task<WechatPaySign> GetPaySign(string appid, string key, string prepayId)
        {
            RequestHandler paysignReqHandler = new RequestHandler();

            // 设置支付参数
            string timestamp = TenPayV3Util.GetTimestamp();
            string nonceStr = TenPayV3Util.GetNoncestr();

            var wechatPaySign = new WechatPaySign()
            {
                NonceStr = nonceStr,
                Timestamp = timestamp,
                Package = string.Format("prepay_id={0}", prepayId),
                SignType = "MD5"
            };

            paysignReqHandler.SetParameter("appId", appid);
            paysignReqHandler.SetParameter("timeStamp", wechatPaySign.Timestamp);
            paysignReqHandler.SetParameter("nonceStr", wechatPaySign.NonceStr);
            paysignReqHandler.SetParameter("package", wechatPaySign.Package);
            paysignReqHandler.SetParameter("signType", wechatPaySign.SignType);

            string paySign = paysignReqHandler.CreateMd5Sign("key", key);
            wechatPaySign.PaySign = paySign;

            return wechatPaySign;
        }

        #endregion

        #region PcPay(PC网站支付)

        /// <summary>
        /// PC网站支付
        /// </summary>
        /// <param name="param">微信支付参数</param>
        /// <returns></returns>
        public async Task<string> PcPay(WechatPayParam param)
        {
            param.TradeType = TradeType.Native;

            var result= await Pay(param);
            return result.CodeUrl;
        }

        #endregion

        #region WapPay(手机网站支付)

        /// <summary>
        /// 手机网站支付
        /// </summary>
        /// <param name="param">微信支付参数</param>
        /// <returns></returns>
        public async Task<string> WapPay(WechatPayParam param)
        {
            param.TradeType = TradeType.MWeb;

            var result= await Pay(param);
            return result.CodeUrl;
        }

        #endregion

        #region PublicPay(公众号支付)

        /// <summary>
        /// 公众号支付
        /// </summary>
        /// <param name="param">微信支付参数</param>
        /// <returns></returns>
        public async Task<WechatPayResult> PublicPay(WechatPayParam param)
        {
            param.TradeType = TradeType.JsApi;
            return await Pay(param);
        }

        #endregion

        #region Pay(微信支付)

        /// <summary>
        /// 调用微信支付，生成预支付交易ID：prepay_id
        /// </summary>
        /// <param name="param">微信支付参数</param>
        /// <returns></returns>
        public async Task<WechatPayResult> Pay(WechatPayParam param)
        {
            string nonceStr = TenPayV3Util.GetNoncestr();
            string notifyUrl = string.Format(param.Domain + param.NotifyUrl);//通知地址，接收微信支付异步通知回调地址
            int totalFee = (int)(param.TotalFee * 100);
            TenPayV3Type tradeType = TenPayV3Type.JSAPI;
            switch (param.TradeType)
            {
                case TradeType.App:
                    tradeType = TenPayV3Type.APP;
                    break;
                case TradeType.JsApi:
                    tradeType = TenPayV3Type.JSAPI;
                    break;
                case TradeType.Native:
                    tradeType = TenPayV3Type.NATIVE;
                    break;
                case TradeType.MWeb:
                    tradeType = TenPayV3Type.MWEB;
                    break;
            }

            //设置参数
            TenPayV3UnifiedorderRequestData requestData = new TenPayV3UnifiedorderRequestData(
                //param.AppId,
                //param.MchId,
                _options.AppId,
                _options.MchId,
                param.Body,
                param.OutTradeNo,
                totalFee,
                param.ServiceIpAddress,
                notifyUrl,
                tradeType,
                param.Openid,
                //param.Key,
                _options.PayKey,
                nonceStr,
                param.DeviceInfo,
                param.StartTime,
                param.ExpireTime,
                param.Detail,
                param.Attach,
                param.FeeType);
            if (param.TradeType == TradeType.MWeb)
            {
                var h5Info = TenPayV3UnifiedorderRequestData_SceneInfo.GetH5InfoInstance<H5_Info_WAP>("Wap");
                h5Info.wap_name = param.WapName;
                h5Info.wap_url = param.WapUrl;
                requestData.SceneInfo = new TenPayV3UnifiedorderRequestData_SceneInfo(true, h5Info);
            }            

            var result = param.TradeType == TradeType.MWeb
                ? await TenPayV3.Html5OrderAsync(requestData)
                : await TenPayV3.UnifiedorderAsync(requestData);
            WechatPayResult wxPayReturn = new WechatPayResult();
            if (result.IsResultCodeSuccess() && result.IsReturnCodeSuccess())
            {
                if (param.TradeType == TradeType.Native)
                {
                    wxPayReturn.CodeUrl = result.code_url;
                }

                if (param.TradeType == TradeType.MWeb)
                {
                    wxPayReturn.CodeUrl = result.mweb_url;
                }
                wxPayReturn.PrepayId = result.prepay_id;
                wxPayReturn.TradeType = param.TradeType;
                return wxPayReturn;
            }

            throw new UserFriendlyException(
                $"微信支付支付失败！err_code：{result.err_code},err_code_des:{result.err_code_des},return_code:{result.return_code},return_msg:{result.return_msg}");
        }

        #endregion

        #region ParseNotify(支付回调)

        /// <summary>
        /// 处理微信支付回调内容
        /// </summary>
        /// <param name="context">Http上下文</param>
        /// <returns></returns>
        public WechatPayReceive ParseNotify(HttpContext context)
        {
            ResponseHandler responseHandler = new ResponseHandler(context);
            string returnCode = responseHandler.GetParameter("return_code");
            string resultCode = responseHandler.GetParameter("result_code");
            string returnMsg = responseHandler.GetParameter("return_msg");

            //验证请求是否从微信发过来（安全）
            if (returnCode != "SUCCESS" && resultCode != "SUCCESS")
            {
                throw new UserFriendlyException(string.Format("微信支付回调，出现异常：{0}", returnMsg));
            }

            return new WechatPayReceive()
            {
                IsSuccess = true,
                Attach = responseHandler.GetParameter("attach"),
                Openid = responseHandler.GetParameter("openid"),
                OutTradeNo = responseHandler.GetParameter("out_trade_no"),
                TimeEnd = responseHandler.GetParameter("time_end"),
                TradeNo = responseHandler.GetParameter("transaction_id"),
                TotalFee = decimal.Parse(responseHandler.GetParameter("total_fee")) / 100
            };
        }

        #endregion

        #region Refund(支付退款)

        /// <summary>
        /// 支付退款
        /// </summary>
        /// <param name="param">微信退款参数</param>
        /// <returns></returns>
        public WechatRefundResult Refund(WechatRefundParam param)
        {
            string nonceStr = TenPayV3Util.GetNoncestr();
            string transactionId = null;
            string outTradeNo = null;
            if (string.IsNullOrEmpty(param.WechatOrderNo))
            {
                outTradeNo = param.OutTradeNo;
            }
            else
            {
                transactionId = param.WechatOrderNo;
            }

            TenPayV3RefundRequestData dataInfo = new TenPayV3RefundRequestData(_options.AppId, _options.MchId,
                _options.PayKey, param.DeviceInfo, nonceStr, transactionId, outTradeNo,
                param.OutRefundNo, (int) (param.TotalFee * 100), (int) (param.RefundFee * 100),
                string.IsNullOrWhiteSpace(_options.Password) ? _options.MchId : _options.Password, null);
            var wxResult = TenPayV3.Refund(dataInfo, _options.CertPath,
                string.IsNullOrEmpty(_options.Password) ? _options.MchId : _options.Password);

            if (wxResult == null)
            {
                throw new UserFriendlyException("微信退款失败");
            }

            var result=new WechatRefundResult();

            if (wxResult.IsResultCodeSuccess() && wxResult.IsReturnCodeSuccess())
            {
                result.OutTradeNo = wxResult.out_refund_no;
                result.WechatOrderNo = wxResult.transaction_id;
                result.IsSuccess = true;
            }
            else
            {
                result.IsSuccess = false;
                result.ErrorMessage = $"微信退款失败！错误码：{wxResult.err_code},错误描述：{wxResult.err_code_des}";
            }

            return result;
        }

        #endregion
    }
}
